vhdl - how to use memory bits for data storage? - Page 1
Forum section: electronics / general

author: aussie_laser_dude
author_rank: Contributor
timestamp: April 23, 2020, 12:02:11 am
content: Hi GuysTotal FPGA noob here.I've written the program and it used ~4000 LUT's and0 memory bitsto store image data.How do I store data to the memory bits?I've attached a screenshot to show the compilation report and another image showing my data storing code.To explain in more detail, I would like to store an integer array or logical vector array in memory and then read/write to those memory bits, this is for a cyclone IV altera fpga. I'm using Quartus II to write vhdl code to display a 2D matrix of data to a monitor through a VGA cable.Thanks for any help

author: oPossum
author_rank: Super Contributor
timestamp: April 23, 2020, 12:25:28 am
content: Make sure all access to the array is clocked. You won't get inferred RAM without a clock (in my experience). Also check the build log for hints why RAM was not inferred.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 23, 2020, 12:39:59 am
content: Yup. You may also want to check that you're not accessing more than two items at the same time - basically anything more than a dual-port memory has a high probability of not being inferred as block RAM.Maybe show us the part of your code which accesses this array.

author: aussie_laser_dude
author_rank: Contributor
timestamp: April 23, 2020, 01:07:21 am
content: Thanks for the posts guys, maybe I didn't explain my question too well.I am a complete beginner who has absolutely no idea on how to read or write any of those 608,526 bits. Does anyone have a working example of how to do it?cheers

author: Daixiwen
author_rank: Frequent Contributor
timestamp: April 23, 2020, 07:40:45 am
content: You need to write your code in a special way to be sure that the synthesizer will recognize it as memory block. As said previously, accesses must be clocked, but there are also other things, like reset conditions, that can make Quartus think it can't do what you want with memory blocks, and resort to logic instead.Have a look at this document:Altera Recommended HDL Coding Styles. It shows how you should write your code, in both VHDL and Verilog, to be sure Quartus recognizes it correctly and use dedicated hardware.Page 13 onwards show how to infer RAM blocks, with code examples.You can also have a look at the synthesizer warnings. As you probably have 1000s or warnings in the log, search for your module name in the log window. If your compiled project doesn't use RAM blocks, you should have a warning explaining why.

author: AndyC_772
author_rank: Super Contributor
timestamp: April 23, 2020, 07:42:37 am
content: There are basically two ways to do this.The first is to write code that behaves exactly the same way as the RAM blocks available in the device, then hope that Quartus notices that some of your logic can be directly implemented using the dedicated RAM hardware, and does so.This is a pretty inexact science, IMHO, and I much prefer the second option which is to use the Megawizard Plug-in Manager to create a memory component to your exact specifications. From here you can use a simple wizard interface to define the width, depth, clocking scheme and other options, and the end result is a VHDL component which you add to your project and instantiate in your top level design.It may be a bit more involved, but you're guaranteed that the RAM blocks will indeed get used.

author: BrianHG
author_rank: Super Contributor
timestamp: April 23, 2020, 07:50:46 am
content: Quote from: AndyC_772 on April 23, 2020, 07:42:37 amAlmost. If you have logic which simplifies any of your addresses feeding to the ram which may always be equal to 1's or 0's, or you are using the read enable/write enable features never enabling them, or your ram data outputs do not affect any logic states in your design which has any effect on any output pin, Quartus'WILL'simplify out (eliminate from your compiled design) part or all of your defined memory block(s). They wont even show up in the compilation report anywhere. Or, maybe by chance (this one is not in my experience), they may show up in the synthesis report, but, magically disappear in the final fitter report.Also, very small rams, may automatically get pushed into logic cells if they are small enough, or, you do not have any FPGA ram resources remaining.

author: Daixiwen
author_rank: Frequent Contributor
timestamp: April 23, 2020, 08:00:08 am
content: There is a drawback in using the Megawizard though: you code becomes specific to Quartus. On the other hand if you try to design your HDL code using the guidelines to have it recognized as a memory block, there is a slight chance that you can also use your code unmodified on FPGAs from another supplier.No one* knows what Intel's long term strategy with the Altera products is, and from what we have seen so far I think it's a good idea to futureproof your code and be ready to move to another plaftorm if (when?) needed.--* probably not Intel either

author: BrianHG
author_rank: Super Contributor
timestamp: April 23, 2020, 08:09:37 am
content: Quote from: Daixiwen on April 23, 2020, 08:00:08 am(For the little more advanced developers)You can still make your own ram sub-module/instance. And in that sub-module, make a compiler platform environmental 'IF' to select which type of vendor specific RAM instance function to be used between Intel, Xilinx, Lattice, & bottom end VHDL as a default fallback.

author: AndyC_772
author_rank: Super Contributor
timestamp: April 23, 2020, 10:10:45 am
content: That's what I'd do. Bear in mind that switching FPGA vendors would be a huge undertaking anyway, involving a new PCB and almost certainly updated firmware in the host system too. I've done designs in which the FPGA has had to grow to the next larger device in the family, but never, ever ripped out a non-trivial design from one vendor in favour of another.If you have a design which calls up an instance of, say, a dual-port RAM, there's absolutely nothing that compels you to use the wizard-generated implementation of that component. You can, if you wish, just write a function that describes the operation of that component in simple (non vendor specific) HDL, and let the synthesis tool take care of inferring the right hardware. Or you can write a 'wrapper' to go around a new vendor's dedicated hardware to make it function like the original vendor's, if it's capable of doing so.As a bonus, doing this means your tried and tested higher level logic remains genuinely unchanged.

author: OwO
author_rank: Super Contributor
timestamp: April 23, 2020, 10:43:58 am
content: No, don't use vendor primitives or megafunction wizard when it can be avoided.You should always enclose commonly used logic in a separate entity. This is what I use for all projects:https://github.com/gabriel-tenma-white/axi-util/blob/master/dcram.vhd

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 23, 2020, 12:44:03 pm
content: Quote from: aussie_laser_dude on April 23, 2020, 01:07:21 amMaybe tell us what exactly you want to achieve. We can't guess.

author: aussie_laser_dude
author_rank: Contributor
timestamp: April 23, 2020, 01:02:44 pm
content: Thanks guys, this was really helpful. The link provided by Daixiwen was great, i get how to do basic internal memory transfers now. Sounds like I'll also need external ddr3 memory for any larger data arrays. I'm kind of understanding the nios ii / qsys / wizard stuff but i suppose I'll try and master the internal memory coding first.Thanks again guys you're all awesome!

author: BrianHG
author_rank: Super Contributor
timestamp: April 24, 2020, 04:14:06 am
content: If you dont need super speed and headaches of DDR3 ram controler, you may try this PSEUDO static ram chip with a 12 wire interface with built in dram controller (run more in parallel or serial switched bank for more speed):https://www.arrow.com/en/products/s27kl0641dabhi023/cypress-semiconductor?utm_term=instock&utm_campaign=arrow_findchips_2019&utm_currency=&utm_medium=aggregator&utm_source=findchips&utm_content=inv_listing

author: aussie_laser_dude
author_rank: Contributor
timestamp: April 24, 2020, 08:34:54 am
content: Wow! $2 for 64Mb of memory, that's pretty tempting actually. To tell a bit about the project I'm making, I'm trying to design a ~300-600MHz (or even higher) digital phosphor oscilloscope using an fpga, external ddr3 memory, ADC for data collection with data displayed directly to a monitor via vga. (This will be continuous streams of large amounts of data being read/written/mathematically processed.My thought's are that it would be nice to have an oscilloscope with the following properties:- High bandwidth (High MHz to low GHz, well, high for a hobby...)- Many channels (at least 4)- 100% waveform display rate with no noticeable lag with display / buttons (try measuring a signal with a loose & moving connection and you'll see it's difficult on a cheap digital scope but easy on an analogue oscilloscope)- be reasonably cheap, open source.Maybe someone's already done it, but this is mostly for fun and learning anyway. Most parts of the project have been coming along nicely (vga driver, learning electronics required for measurements, adc parts / soldering etc) the only thing that's stumped me a bit has been communicating with DDR3 external memory (and internal memory). Pretty excited to try and get the memory transfers all going, that'll be a big step forward

author: MadTux
author_rank: Frequent Contributor
timestamp: October 20, 2021, 12:11:35 pm
content: Can someone please explain this to me??? Using Altera Quartus 13.01, Linux-64 and a tiny EP1C3T144 Cyclone1 FPGA, cheap 35$ board from ebay.Basically want to use a 256x8 ROM/block RAM as text ROM to display on a 20x2 LCD, so far more for goofing around trying to learn VDHL than for serious application....ROM/block RAM is initialized from 256x8 mif fileAlso using ROM data to blink 6 LEDs at about 10Hz.The weird thing is that everything is displayed correctly on LCD, LEDs are also blinking accordingly to the data in ROM, yet the report tells me, no block RAM is used.ROM is present in RTL viewer, but missing in chip planer and synthesis report. No way that thing works on 338LUTs only, either the LUT report is wrong or it's secretly using block RAM without telling me???Block RAM is reported correctly/present in chip planer if used directly without being inside "if statement", amount of LUTs used changes insignificantly and LEDs blink the same way....

author: SMB784
author_rank: Frequent Contributor
timestamp: October 20, 2021, 01:58:18 pm
content: If the ROM is small, or not instantiated correctly, Quartus/Vivado will often just implement it in registers rather than BRAMs. That may be what is going on here. Check your synthesis output logs to see whats happening to the ROM during synthesis/implementation

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 20, 2021, 05:56:03 pm
content: There is zero guarantee the tools will map any memory described in HDL as block RAM. It may or may not, depending on memory size/width, speed constraints, code style (yes, to make the tools infer BRAM, some HDL styles are often required), etc.I don't know Quartus, but for most FPGAs I've dealt with, there's usually an attribute you can define for your memory to "force" inferring block RAM.But note that unless it eats up a lot of LUTs/registers, or does not meet timing requirements, you should usually not bother. Distributed memory is fine, and for something that small (256x8), really, you shouldn't bother.Now are people actually still using Cyclone 1 FPGAs?(just a question, I don't know how many LUTs they have.)

author: emece67
author_rank: Frequent Contributor
timestamp: October 20, 2021, 06:57:19 pm
content: .

author: BrianHG
author_rank: Super Contributor
timestamp: October 20, 2021, 07:43:28 pm
content: Here is the correct google search term:'quartus vhdl infer ram attribute'You may tell quartus to infer any array into a ram block of your choice. Though, make sure that choice will match the FPGA you are compiling for or just try MLAB.

author: MadTux
author_rank: Frequent Contributor
timestamp: October 25, 2021, 10:43:03 am
content: Thanks for the ideas, guys!What I found so far, it mostly comes down mostly to coding style, whether blockRAM is implemented or not.Trying to force blockRAM implementation with following code didn't work, Quartus does it's thing and doesn't care....Code:type lcdram16x8 is array (255 downto 0) of std_logic_vector (7 downto 0);signal lcd16x8: lcdram16x8;attribute ramstyle : string;attribute ramstyle of lcd16x8 : signal is "M4K";Quartus doesn't like conditional blockRAM inside if/case/... statements, what works is to read blockRAM directly and use the result conditonally.I.e: Read "LCD_rom_out <= lcd16x8(to_integer(unsigned(LCD_charcounter)));" directly without conditonal statements.And put "LCD_data <= LCD_rom_out;" inside conditional statements.Code that gets Block RAM implemented:Code:process (t20us_SQW) --LCD writebeginif (t20us_SQW'Event and t20us_SQW = '1') then--if (f10Hz_SQW'Event and f10Hz_SQW = '1') thenLCD_rom_out <= lcd16x8(to_integer(unsigned(LCD_charcounter)));if (LCD_enable_SQW = '1') thenLCD_enable_SQW <= LCD_enable_SQW XOR '1';if (LCD_init = '1') then --initial mode--LCD_charcounter <= LCD_charcounter+'1';case LCD_charcounter(5 downto 0) iswhen "000000" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00111000"; --001ABC00, A=interface lenght, B=Number of display lines, C=Font selectionLCD_charcounter <= LCD_charcounter+'1';when "000001" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00001100";--00001ABC, A=Display ON=1/OFF=0, Cursor ON=1/OFF=0, Blinking Cursor character ON=1/OFF=0LCD_charcounter <= LCD_charcounter+'1';when "000010" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00000110";--000001AB, A=Display write address increment ON=1/OFF=0, B=Display shift/rolling ON=1/OFF=0LCD_init <='0'; --initialisation completeLCD_charcounter <= "000000" ;when "111111" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00000010";--Cursor return to startLCD_init <='1'; --initialisation restartLCD_charcounter <= "000000" ;when others => LCD_RS <='1';LCD_charcounter <= "000000" ;end case;elsif (LCD_init = '0') then --initial modecase LCD_charcounter(5 downto 0) iswhen "111111" => LCD_init <='1'; --initialisation restart--LCD_charcounter <= "000000" ;when others =>LCD_RS <='1';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <= LCD_rom_out;LCD_charcounter <= LCD_charcounter+'1';end case;--ROM_addr <= LCD_dcounter;--LED5 <= '1';--LCD_data <="10101010";--rom_counter <= rom_counter+'1';--rom256x8_out <= rom256x8(to_integer(unsigned(LCD_dcounter)));--LCD_data <= rom256x8(to_integer(unsigned(LCD_dcounter)));end if;elseLCD_enable <='0' ;LCD_enable_SQW <= LCD_enable_SQW XOR '1';end if;end if;end process;If you want to read the blockRAM directly "LCD_data <= lcd16x8(to_integer(unsigned(LCD_charcounter)));" inside conditional statement structure, it gets implemented in huge logic block. FAIL!Code:process (t20us_SQW) --LCD writebeginif (t20us_SQW'Event and t20us_SQW = '1') then--if (f10Hz_SQW'Event and f10Hz_SQW = '1') then--LCD_rom_out <= lcd16x8(to_integer(unsigned(LCD_charcounter)));if (LCD_enable_SQW = '1') thenLCD_enable_SQW <= LCD_enable_SQW XOR '1';if (LCD_init = '1') then --initial mode--LCD_charcounter <= LCD_charcounter+'1';case LCD_charcounter(5 downto 0) iswhen "000000" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00111000"; --001ABC00, A=interface lenght, B=Number of display lines, C=Font selectionLCD_charcounter <= LCD_charcounter+'1';when "000001" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00001100";--00001ABC, A=Display ON=1/OFF=0, Cursor ON=1/OFF=0, Blinking Cursor character ON=1/OFF=0LCD_charcounter <= LCD_charcounter+'1';when "000010" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00000110";--000001AB, A=Display write address increment ON=1/OFF=0, B=Display shift/rolling ON=1/OFF=0LCD_init <='0'; --initialisation completeLCD_charcounter <= "000000" ;when "111111" => LCD_RS <='0';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <="00000010";--Cursor return to startLCD_init <='1'; --initialisation restartLCD_charcounter <= "000000" ;when others => LCD_RS <='1';LCD_charcounter <= "000000" ;end case;elsif (LCD_init = '0') then --initial modecase LCD_charcounter(5 downto 0) iswhen "111111" => LCD_init <='1'; --initialisation restart--LCD_charcounter <= "000000" ;when others =>LCD_RS <='1';LCD_RW<='0' ;LCD_enable<='1' ;LCD_data <= lcd16x8(to_integer(unsigned(LCD_charcounter)));--LCD_data <= LCD_rom_out;LCD_charcounter <= LCD_charcounter+'1';end case;--ROM_addr <= LCD_dcounter;--LED5 <= '1';--LCD_data <="10101010";--rom_counter <= rom_counter+'1';--rom256x8_out <= rom256x8(to_integer(unsigned(LCD_dcounter)));--LCD_data <= rom256x8(to_integer(unsigned(LCD_dcounter)));end if;elseLCD_enable <='0' ;LCD_enable_SQW <= LCD_enable_SQW XOR '1';end if;end if;end process;Conditionally writing BlockRAM should work similarly, by evaluating the data written, store it in a register and then write if direcly in the next cycle, without using any conditional statementsSmallest Cyclone1 (EP1C3T144) has about 3000LUTs and 60kBit BlockRAM, more than plentiful for small projects, especially if you are used to CPLDsSomewhat hard to fill all that logic in small projects, if not wasted as memory. Also like the facts that it's still TQFP and thereby easy solderable/exchangeable if fried.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 25, 2021, 02:21:07 pm
content: Quote from: MadTux on October 25, 2021, 10:43:03 amIt is not coding style.You read from RAM at one clock edge and then process the data you have read at the next clock edge.Before the change, you tried to read from RAM and process the data at the same clock edge, which is impossible. So, the tools implemented what you wanted, but could not use RAM for this.

author: BrianHG
author_rank: Super Contributor
timestamp: October 25, 2021, 02:35:30 pm
content: I do not know how to do thin in VHDL, but here is how it would be done in systemverilog.(*preserve*) logic [7:0] ram_read ;always_comb ram_read = memory;The 'preserve' forces logic cells from a clock-less ram block, otherwise:logic [7:0] ram_read ;always_ff @(posedge CLK) ram_read <= memory;Would be a standard clocked registered ram read.In my first case, without the 'preserve' attribute, Quartus will try to incorporate the array 'memory' as logic in your code since that array can be simplified down to the surrounding logic as if the entire array block was combinational logic itself. In the second case, the 'preserve' is not needed as there is a clocked blocking ' <= ' latch.

author: MadTux
author_rank: Frequent Contributor
timestamp: October 26, 2021, 09:08:14 pm
content: Quote from: NorthGuy on October 25, 2021, 02:21:07 pmWhy not? If building a design with TTL chips, I could very well do logic functions (put couple of couple of logic gates/adder/whatnot... on the data bus....) on data as it comes out of the data bus. Quartus simply implements that in logic instead of blockRAM. What's impossible is doing multiple latched operations in same clock cycle, which I didn't do, otherwise it wouldn't be possible to implement.In TTL, it should be also possible to discard/skip data reading/writing at certain addresses (disable /OE or /WE line), Quartus doesn't like that as blockRAM. The solution is to read the data nevertheless and discard it afterwards, which gets implemented in blockRAM.

author: james_s
author_rank: Super Contributor
timestamp: October 26, 2021, 09:25:17 pm
content: Quote from: Daixiwen on April 23, 2020, 08:00:08 amIf you directly instantiate the RAM it becomes specific to the architecture. At least when using the megawizard it is usually trivial to use the equivalent wizard in Xilinx or whatever other family you're using. When I started out with FPGA development I did quite a bit of porting projects from one family to another. Xilinx ISE and Altera Quartus each have a similar wizard for defining things like RAM and ROM.

author: Someone
author_rank: Super Contributor
timestamp: October 26, 2021, 10:18:27 pm
content: Quote from: MadTux on October 26, 2021, 09:08:14 pmIts what you described in the code, by declaring the LCD_data as a register:In the first case you add an additional register on the ram/rom read LCD_rom_out..which differently-aligns the read address to the data in the second case so they aren't even equivalent..In the second case LCD_data is muxed from constants and the combinatorial ram/rom read.Block rams don't have an async read capability, so they cant be used for your second case. Yes, you could declare the registers on the other side of the mux and it would work fine, a clever toolchain might even optimise that for you knowing it wants the register in the ram. But you didn't do that, you asked for and were given an async ram/rom. I'll bet the output log file from the tools make this very explicit to you (but you never read them).

author: MadTux
author_rank: Frequent Contributor
timestamp: October 26, 2021, 11:59:19 pm
content: Code is a mess, I know ;-)But happens, if you playing with lots of things at once: getting blockRAM/ROM implemented, display reasonable numbers on LCD, goofing with onchip PLL....Guess I'll rewrite everything once LCD works and be careful how on how to use blockRAM.PLL works fine btw, overclocked that Cylcone1 to 400MHz ;-), gave the PLL wizzard some 25MHz on clock input, while in reality, it's 50MHz, so it doesn't bitch about it. And guess what, it still works, doesn't even get warm, lol.Perhaps I'll try what's the real limit of these C1s, but 400MHz vs official 275MHz is quite promising ;-) Guess I'll have to pick my Tek7904A/7A29 or even HP8658B for freq measurements from now on, little Hameg 1507 on my bench doesn't do it at these freqs ;-DWTF, works even @ 500MHz, gets a little warm, though ;-)

author: BrianHG
author_rank: Super Contributor
timestamp: October 27, 2021, 01:10:10 am
content: I thought the limit was 472MHz. This is what's stated in most Cyclones and Max10s ds as well. It is also the reason why my DDR3 controller works at 500MHz and my HDMI transmitter maxes out at 540MHz as it's only a few % higher.

author: NorthGuy
author_rank: Super Contributor
timestamp: October 27, 2021, 03:13:41 pm
content: Quote from: MadTux on October 26, 2021, 09:08:14 pmThat's exactly what I said.