Jump to content
  • 0

Change ROM COE


dcc

Question

Hi all,

My design is HDL only with no processor. I have large ROMs in the project and need to change the COE files all the time. IS there a way to do this without having to regenerate the core and compile from scratch?? Each change costs me 40 minutes.

I know with Data2MEM you can do that without having to regenerate the core and compile the design from scratch (IF you have a processor), but that does not apply to my design.

Thanks in advance for your help.

 

 

Link to comment
Share on other sites

10 answers to this question

Recommended Posts

Hi! I too share your pain :-)

I now infer all my ROMs - using code like this:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

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

architecture Behavioral of beacon is
    type a_data is array(0 to 31) of std_logic_vector(7 downto 0);
    signal data : a_data := (
                x"01",x"02",x"04",x"08",x"10",x"20",x"40",x"80",
                x"01",x"02",x"04",x"08",x"10",x"20",x"40",x"80",                
                x"01",x"01",x"02",x"02",x"04",x"04",x"08",x"08",
                x"10",x"10",x"20",x"20",x"40",x"40",x"80",x"80");
    signal counter : unsigned(26 downto 0) := (others => '0');
begin

process(clk)
    begin
        if rising_edge(clk) then
            leds    <= data(to_integer(counter(counter'high downto counter'high-4)));
            counter <= counter+1;
        end if;
    end process;
end Behavioral;

This is something I had open right now - the counter is longer than it would need to be for a ROM address, passed in as a parameter for the module.

It isn't very much work to make a script that converts your source data (in whatever format) into the entire Verilog/VHDL module, and it means that your code will work with other FPGA vendors tooling.

If you track down some Arcade Game implementations for an FPGA they usually have a script to do exactly this - it is against the rules to include the ROM dumps in the code base. For example - https://github.com/bhamadicharef/bombjack-papilioplus-fpga/tree/master/romgen_source

Link to comment
Share on other sites

@dcc,

The information provided by Hamster is fine for some simple applications. I often use block memory as ROM , single port or dual port RAM. Sometimes you can use FPGA agnostic HDL as he shows and sometimes you want to have control over how block memory resources are used. ( I'm not entirely sure that the code snippet above will use block memory or distributed memory; I suspect that that particular code will use distributed memory ). Each FPGA vendor lets you know how to write HDL to instantiate the kind of memory you want. There's one way to find out for sure of course and that's to experiment with the tools.

For some reason Vivado does not recognize when a .coe initialization file associated with a .xco primative has changed. ( I'm assuming that you used the IP generator to create your ROM and have the .xco as a source file....) Unless there's some setting in Vivado that I'm unaware of or hasn't been posted anywhere I've found that I have to modify the IP in Vivado, change the location of the coe file to the new one ( or edited old one ) and re-generate.

I am currently working on a programmable controller with a DPRAM program store. I am using a UART to update the contents but could use the USB 2 DPTI as well. This is one way to change a ROM, if you can implement it as a DPRAM. The ICAPE2 interface might be another way to accomplish the same thing without implementing your ROM as DPRAM... I've not used ICAPE2 as of today.

As far as Vivado irritations go this is a minor one for my purposes. If you discover a better way let us all know.

Link to comment
Share on other sites

Data2Mem doesn't need a CPU to work.  If the ROM is very large, you might need to become more of an expert on the bmm file format.  My use-case was storing build info on the fpga -- version, build time, design name, timing score, and a control register map with names/types/addresses.  A single BRAM is 4kB to 4.5kB and the data stored on it could be compressed.

I'm not sure if you are using ISE or Vivado.  I don't recall exactly how to set this up as it is something I've very few times.  Basically once per project.

Glancing at some documentation, for ISE you need to create a .bmm file.  Just pretend you have a processor.  

I suggest using BRAM primitives for the ROM.  This is because the .bmm file needs the name and configuration of BRAM inside the ROM.  I also suggest adding the synthesis attribute that keeps hierarchy for the BRAM instance/instances or the module they are in.

For ISE, you can specify the .bmm file for ngdbuild.  BitGen has an option to update the ROM as well.  I don't know exactly how to update the BRAM using data2mem, but you would need to find the annotated bmm file.

Link to comment
Share on other sites

@dcc,

One other thought. I realize that you want to let Vivado take care of your ROM during synthesis. This is what one would expect for a ROM. I'm assuming that the issue is when you are doing development work that involves modifying the ROM contents. Sometimes you just have to work around tool limitations and bugs. Having production code and development code is sometimes the easiest solution to reducing development time. A DPRAM for development and a standard ROM for production might alleviate your hassle. Of course I'm making broad assumptions about the target resources as you haven't mentioned what hardware you are using. A UART loader approach only requires 2 spare IO pins and a LVCMOS compatible USB Serial cable.

Link to comment
Share on other sites

For the definitive word on how to infer memory blocks, including code examples, have a look at https://www.xilinx.com/support/documentation/sw_manuals/xilinx2014_1/ug901-vivado-synthesis.pdf from page 95. It covers everything from the simplest single port RAMs/ROMs to dual port RAMs with different data widths, with byte enables. For the large part, the same patterns also work in ISE.

page 148 & 149 might be of special interest. It has  "Initializing Block RAM From an External Data File VHDL Coding Example" and "Initializing Block RAM From an External Data File Verilog Coding Example"

Link to comment
Share on other sites

These both work for full synthesis runs where the data is known before synthesis starts.  

Consider the case where you have the same processing core for five different communication frequencies.  The only difference between the cores in this example would be some coefficients.  It would be better to spend 40 + 4 = 44 minutes to get all five .bit files than to spend 5*40 = 200 minutes to get all five.  The goal here is to avoid synthesis/implementation and instead just update the BRAM content.

For my use-case, I did not have the content for the BRAM at synthesis time -- it included timing score and other build stats that were only available after synthesis/implementation was complete.

Link to comment
Share on other sites

@hamster,

I am certainly with you with regard to using Xilinx documentation to see how the syntheses tool interprets HDL. There are numerous documents to view. As with all Xilinx documentation one should not take everything as absolute truth however. I refer you to this thread: https://forums.xilinx.com/t5/Synthesis/UG901-distributed-vs-block-ram-inference/td-p/775730. 

You have to read though to the end to get past the superfluous commentary. So while you may rely on the synthesis tool to infer distributed RAM by not clocking the read data that synthesis tool might infer a latch and use block RAM anyway. I think that we can all agree that we care about which one we want to store 10 data words. The only real way to know is to write some test HDL, go through synthesis, place and route and implementation. Then you can request a utilization report from the implementation view to see what resources were actually used. Life always seems to be a bit more complex than it should be. In the document that you refer to the "Single-Port RAM with Asynchronous Read VHDL Coding Example" and the "Single-Port Block RAM Read-First Mode VHDL Coding Example" are given the same source HDL; File:HDL_Coding_Techniques/rams/rams_04.vhd.  While you can argue that this faux pas is bothersome but further reading will reveal the correct HDL syntax this is not completely true for RAM resources as the thread makes clear.

I just let Vivado IP take care of block ram instantiations because I get what I expect. But if you want to take the time to verify the Xilinx documentation and proceed as guided then this is Ok as well.

While I not only like Hamsters approach to writing device and vendor agnostic HDL and preach it,  my experience tells me to proceed with caution. This is especially true for block ram resources. Even within Xilinx devices not all block memory has the same configuration and the synthesis tools are not all the same from ISE version to version of Vivado version to version. Again, do your homework, test and verify.

None of this commentary has relieved poor dcc of his problem but the discussion is certainly interesting....

 

 

 

 

Link to comment
Share on other sites

The issue raised by dcc is a cautionary tale for all Xilinx tools users. Be aware that the documentation is frequently wrong, lacking details, or applicable to specific versions of specific Xilinx tool sets. Some documents are more reliable than others and there might be a lot of documents that one has to consult on a particular topic. The DATA2MEM documentation only applies to ISE and the current documents related to it are old. After finding and pouring through a lot of documents it is common to have to search though a lot of Xilinx support answer records and user forum threads to get to a conclusion... and the conclusion is often not consistent with the documentation. Maintaining documentation is problematic for everyone. In ISE there is a clear path to changing block memory resource initialized contents using Impact. This avoids having to rebuild the configuration file. It may not work for all versions of ISE. The only way to find out is to do research the relevant documentation look for applicable examples and experiment. Don't make assumptions. When a document suggests that Vivado might work but all of the text references ISE be very cautious.

With Vivado there is no way to alter a bit file to change block memory initialized contents that I am aware of. In Vivado 2016.2 if you try to force a re-generation of a block ram based IP because the coe file has been changed Vivado will tell you " Naw, not gonna do that cause there's nothing to do". Modifying the IP by going though the steps for selecting the same coe initialization file does work. The bottom line for dcc is that in Vivado if you want the bit file to contain a specific version of initialized contents you will have to go through re-generation of IP, synthesis, implementation, and bitgen. The only way around this is to build an alternate post-configuration path to modifying the clock ram contents. For ISE users you are aware that you are using tool flows that are no longer supported.

It's time consuming but pouring through the Xilinx community user's forums does often provide an answer to a dilemma. The Xilinx answer record archive sometimes provides an answer. The documentation sometimes provides an answer. This FPGA development stuff is complicated.

Link to comment
Share on other sites

I'm trying to remember how exactly to do this.  From what I recall, you don't need to have a processor or can just lie about it.  IIRC as long as you don't choose something that has the wrong endian you'll be ok.  IIRC, that info is mostly used for jtag, bsp, and other sdk related stuff.  I know data2mem allowed odd-sized busses as well, although I don't recall what was required.  I think it just worked.  At least valid non-weird sizes, not >36b.

Vivado allows you to specify a bmm file for use in the project.  This can be scoped to ref if desired.  Scoped to ref is nicer if you re-use the IP as the bmm file doesn't need to include its parent path.  It sounds like adding this for synthesis/implementation is enough to get the annotations.

After that you should get an annotated bmm file in a predicatable location.  It sounds like you can just call the tcl command "write_mem_info" to get a .mmi file from this.

From there, you can run updatemem.  Perhaps using exec from a tcl script.

 

That said, this method relies on knowing the ram configuration and possibly writing a script to convert a coe into a potentially complex bmm and mem file.  Inferring a ROM or using coregen would make this more difficult.  Inferred memory might be unreliable, as some implementation flows could change the bram configuration from run to run.  It also sounds like the xilinx primitive macros also generate these bmm/mmi files, so it is possible you can just use them and write a script to updatemem later.

 

The final option is to have a build flow that sets the initialization parameters of the BRAM post-placement.  This apparently is useful if you need to have encrypted bit files as well.

Link to comment
Share on other sites

@Piasa,

Thanks for posting your thoughts on this topic. One thing about DATA2MEM is that since the most recent documentation is from 5 years ago Xilinx hasn't felt the need to support it. For my application the block memory is 48 bits wide and re-generating the IP and going through all of the steps to recreate a bit file, while a nuisance, is tolerable. I can do development around this by loading data post configuration and executing the program store in hardware.

Link to comment
Share on other sites

Archived

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

×
×
  • Create New...