VHDL assigning to and from array gives "XXXX" - Page 1
Forum section: electronics / general

author: ivan747
author_rank: Super Contributor
timestamp: September 27, 2023, 11:20:46 pm
content: Hi everyone,I am writing a small dual port memory. If both the read address rdptr and write address wrptr are set to the same location, the data output is "XXXXXXXXX". Why is this?I suspect this happens every time I try to assign to and from the same array location at the same time. I am simulating this on ModelSim 10.5 if that matters.Code:entity fifo isport(DataIn:in std_logic_vector(8 downto 0);DataOut:out std_logic_vector(8 downto 0);rden, wren:in std_logic[...]);end entity fifo;architecture rtl of fifo istype fifo_array is array(7 downto 0) of std_logic_vector(8 downto 0);signal fifo: fifo_array;[...]beginwrProc: process(wrptr,wren) isbeginif(wren = '1') thenfifo(wrptr) <= DataIn;end if;end process wrProc;rdProc: process(rdptr,rden) isbeginif(rden = '1') thenDataOut <= fifo(rdptr);end if;end process rdProc;[...]end rtl;The fix I came up with is adding an if statement to catch this condition.Code:wrProc: process(wrptr,wren) isbeginif(wren = '1') thenfifo(to_integer(wrptr)) <= DataIn;end if;end process wrProc;rdProc: process(rdptr,rden) isbeginif(rden = '1') thenif(rdptr = wrptr) then-- We need this to notDataOut <= DataIn; -- get XXXX on DataOutelseDataOut <= fifo(to_integer(rdptr));end if;end if;end process rdProc;[...]This works, but it doesn't feel very elegant to me. I'm having the feeling this will add adding an implicit multiplexer and comparator that should be unnecessary.Any insights? I'm curious about why this is happening. It looks like one of those traps for young players that Dave always mentions.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 28, 2023, 12:44:00 am
content: This comes from the fact your two processes are clockless. Which is definitely not a very good idea here.

author: ivan747
author_rank: Super Contributor
timestamp: September 28, 2023, 01:32:58 am
content: Quote from: SiliconWizard on September 28, 2023, 12:44:00 amThanks! I just noticed that if I add DataIn to the sensitivity list of wrProc and add fifo to the sensitivity list of rdProc, some of the weird behavior goes away (it's no longer latching on the last value of DataIn or fifo respectively). But adding a huge piece of memory to a sensitivity list sounds negligent.I moved over to conditional assignments and it's much better now:Code:fifo(to_integer(wrptr)) <= DataIn when wren = '1';DataOut <=fifo(to_integer(rdptr)) when rden = '1'; else"ZZZZZZZZZ" when rden = '0' else"UUUUUUUUU";There is still an implied latch when writing but that's the point of memory, I guess.I also had a bug in a section of the code I haven't posted here, that dealt with addresses. In one of the process statements I was incrementing the write address using the increment read address signal. I still don't know why the data output would be "XXXXXXXXX" though, I can't think of a way that would be possible. Who knows, I'm kind of tired and maybe missed a double assignment. That's my only explanation.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 28, 2023, 06:59:48 am
content: Yes, but I'd still recommend implementing clocked memories for anything used outside of pure simulation.You have implemented a combinatorial dual-port memory, which will give you poor performance (low Fmax) and a lot of potential timing issues in real-life implementations. Don't do that, or do that only if there's an absolutely good reason for doing so.

author: AndyC_772
author_rank: Super Contributor
timestamp: September 28, 2023, 07:38:44 am
content: Quote from: ivan747 on September 28, 2023, 01:32:58 amIt's completely normal to have a process be sensitive to every signal that's referenced within it. VHDL-2008 allows 'process (all)' as a shortcut.I'll echo what others have said, though... you *really* need a clock for this to ever work in practice. As a bonus, it might even then be able to use the hard RAM blocks that most FPGAs include, rather than soaking up a huge amount of expensive, asynchronous logic.

author: ivan747
author_rank: Super Contributor
timestamp: September 28, 2023, 03:09:21 pm
content: Thanks for the feedback! I went back to having a clock for the actual register access and it's everything is closer to what it should be.There are other sections of the component that I'm having problems with but that's because the spec isn't very specific for some timings, and I'm having to guess from the test bench. But don't worry about that, it's my homework.Something that caught my attention is that I still have to do the conditional assignment for some reason:It was timingCode:rwProc: process(clk,rdptr, wrptr, wren, full) isbeginif(rising_edge(clk)) thenif(wren = '1') thenfifo(to_integer(wrptr)) <= DataIn;end if;if(rdptr = wrptr) thendmuxout <= DataIn;elsedmuxout <= fifo(to_integer(rdptr));end if;-- If I don't add this condition, dmuxout is assigned "UUUU" when rdptr=wrptrend if;end process rwProc;But overall less issues than before.It was timing, I was writing to one address and reading from the next one which is undefined yet.I'll try to break these into two separate processes (read and write) and see what happens.

author: ivan747
author_rank: Super Contributor
timestamp: September 28, 2023, 03:36:26 pm
content: Ok, I fixed the timings and the original problem came back. But, I noticed that I only get "XXXXXXXXXX" when I assign (maybe latch?)toandfromthe same memory pointer on the same clockcycleedge. Even if I put reading and writing on separate processes.The problem is when I'm reading with a clock I am creating a latch implicitly, so if read and write to the same location is done on the same edge the read is delayed by one clock cycle, and the test bench tests the reads after 3/4 of a cycle. I think the "XXXXXXXXX" output happens because I was assigning "UUUUUUUUU" to the output signal because of the delay.Another way of looking at is is that signal assignments happen after the process block is done. Because the process block is not done, I am reading from the previous value of the fifo, not the value I just assigned when writing.So I fixed it by writing on the rising edge of the clock and reading on the falling edge of the clock. The other solution is to have asynchronous reads.By the way do FPGAs implement all the states of std_logic? Sounds a little wasteful but at the same time they also have some tri-state circuits.

author: AndyC_772
author_rank: Super Contributor
timestamp: September 28, 2023, 09:11:39 pm
content: It's important to recognise that the behaviour of VHDL is well defined.Within a clocked process, the value of every signal is effectively frozen immediately before the instant the active clock edge occurs. So, for example, the classic 'how not to swap two variables' example:Code:if rising_edge (clk) thenb <= a;a <= b;end if...actually DOES work, because the two assignments aren't executed one after the other like they would be if the code were being executed on a CPU. They don't both end up equal to the original value of 'a' like you might expect.Instead, what the code means is: at the instant the clock arrives, 'b' takes the value that 'a' had immediately before the clock, and 'a' takes the value that 'b' had immediately before the clock.With a DPRAM, if you read and write the same address on the same edge, you'd normally expect the output to equal the old contents of the address being accessed, because at the instant the clock arrives the new value hasn't been latched into memory yet.By explicitly including a condition that the output should equal the new input if both the read and write pointers are equal, you're effectively bypassing the memory entirely - and that's completely OK if that's how you want your logic to work. Some FPGAs actually include this 'shoot through' or 'bypass' feature when you instantiate their internal RAM blocks.

author: ivan747
author_rank: Super Contributor
timestamp: September 29, 2023, 01:55:21 pm
content: Quote from: AndyC_772 on September 28, 2023, 09:11:39 pmThis is my case, the test bench that validates my work is expecting the output to equal the input within the same clock cycle.Thank you very much for the explanation. I hadn't seen that mentioned in the book.Much appreciated!

author: asmi
author_rank: Super Contributor
timestamp: September 29, 2023, 06:29:35 pm
content: In Xilinx 7 series devices BlockRAM can be configured in WRITE_FIRST, READ_FIRST or NO_CHANGE write mode on a per-port basis. WRITE_FIRST is the default mode, in which the written value shows up at the output ("write before the read"), READ_FIRST outputs value that was there before the write ("read before the write"), NO_CHANGE keeps whatever value there was as a result of a previous read operation.However, there is a warning:Quote