Electronics > FPGA

vhdl - how to use memory bits for data storage?

<< < (5/6) > >>

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

--- End code ---

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 write
begin
if (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') then
LCD_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) is
when "000000"  =>  LCD_RS <='0';
LCD_RW<='0' ;
LCD_enable<='1' ;
LCD_data <="00111000"; --001ABC00, A=interface lenght, B=Number of display lines, C=Font selection
LCD_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=0
LCD_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=0
LCD_init <='0'; --initialisation complete
LCD_charcounter <= "000000" ;

when "111111"  => LCD_RS <='0';
LCD_RW<='0' ;
LCD_enable<='1' ;
LCD_data <="00000010"; --Cursor return to start
LCD_init <='1'; --initialisation restart
LCD_charcounter <= "000000" ;

when others =>  LCD_RS <='1';
LCD_charcounter <= "000000" ;
end case;

elsif (LCD_init = '0') then  --initial mode
case LCD_charcounter(5 downto 0) is
when "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; 

else
LCD_enable <='0' ;
LCD_enable_SQW <= LCD_enable_SQW XOR '1';
end if;
end if;
    end process;

--- End code ---

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 write
begin
if (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') then
LCD_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) is
when "000000"  =>  LCD_RS <='0';
LCD_RW<='0' ;
LCD_enable<='1' ;
LCD_data <="00111000"; --001ABC00, A=interface lenght, B=Number of display lines, C=Font selection
LCD_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=0
LCD_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=0
LCD_init <='0'; --initialisation complete
LCD_charcounter <= "000000" ;

when "111111"  => LCD_RS <='0';
LCD_RW<='0' ;
LCD_enable<='1' ;
LCD_data <="00000010"; --Cursor return to start
LCD_init <='1'; --initialisation restart
LCD_charcounter <= "000000" ;

when others =>  LCD_RS <='1';
LCD_charcounter <= "000000" ;
end case;

elsif (LCD_init = '0') then  --initial mode
case LCD_charcounter(5 downto 0) is
when "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; 

else
LCD_enable <='0' ;
LCD_enable_SQW <= LCD_enable_SQW XOR '1';
end if;
end if;
    end process;

--- End code ---

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 statements

Smallest Cyclone1 (EP1C3T144) has about 3000LUTs and 60kBit BlockRAM, more than plentiful for small projects, especially if you are used to CPLDs  ;D
Somewhat 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.

NorthGuy:

--- Quote from: MadTux on October 25, 2021, 10:43:03 am ---What I found so far, it mostly comes down mostly to coding style

--- End quote ---

It 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.

BrianHG:
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[address];

The 'preserve' forces logic cells from a clock-less ram block, otherwise:

logic [7:0] ram_read ;
always_ff @(posedge CLK) ram_read <= memory[address];

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.

MadTux:

--- Quote from: NorthGuy on October 25, 2021, 02:21:07 pm ---
--- Quote from: MadTux on October 25, 2021, 10:43:03 am ---What I found so far, it mostly comes down mostly to coding style

--- End quote ---

It 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.

--- End quote ---

Why 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.

james_s:

--- Quote from: Daixiwen on April 23, 2020, 08:00:08 am ---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 ;)

--- End quote ---

If 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.

Navigation

[0] Message Index

[#] Next page

[*] Previous page

There was an error while thanking
Thanking...
Go to full version