Forum Discussion
Altera_Forum
Honored Contributor
10 years ago --- Quote Start --- You should observe your RTL diagram to find answer. if I were you: (brainstorm, suggestions) 1)try out make only one assignment to ram. 2)your read two addresses . Are both really needed in the same time? if not, you should read ram only once. 3)it seems tool believes that iseld and isels1, isels2 could be the same. Using attribute for assignments implied from code sometimes bring another attribute . please check Analysis&Synthesis report for additional signal attribute that comes together. --- Quote End --- Thanks for your kind response. Only having one assignment to RAM does not seem to do the trick. Reading from two addresses is quite necessary to fetch data from two CPU registers and I'd assume that dual port RAM can handle that, although I think that reading twice from the same address may be troublesome. If desperate I can distribute the two reads over clock cycles at the expense of performance, but I'd prefer to avoid that. The register selection signals (selS1, selS2 and selD) can indeed assume the same values. In the following version I tried to make sure that
- only one read is specified in the case that the two read addresses are the same
- it should be clear that read and write are mutually exclusive in logic
library IEEE;
use IEEE.std_logic_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
library work;
use work.constants.all;
entity registers is
Port(
I_clk: in std_logic;
I_en: in std_logic;
I_op: in std_logic_vector(1 downto 0);
I_selS1: in std_logic_vector(4 downto 0);
I_selS2: in std_logic_vector(4 downto 0);
I_selD: in std_logic_vector(4 downto 0);
I_dataAlu: in std_logic_vector(XLEN-1 downto 0);
I_dataMem: in std_logic_vector(XLEN-1 downto 0);
O_dataS1: out std_logic_vector(XLEN-1 downto 0);
O_dataS2: out std_logic_vector(XLEN-1 downto 0)
);
end registers;
architecture Behavioral of registers is
type store_t is array(1 to 31) of std_logic_vector(XLEN-1 downto 0);
signal regs: store_t := (others => X"00000000");
attribute ramstyle : string;
attribute ramstyle of regs : signal is "no_rw_check"; -- why does Quartus still add pass-through logic?
begin
process(I_clk)
variable data: std_logic_vector(XLEN-1 downto 0);
begin
if rising_edge(I_clk) and I_en = '1' then
-- TODO: find out why synthesis sees read-during-write behavior
if I_op = REGOP_READ then
-- we read, we don't write!
if I_selS1 = I_selS2 then
-- both selects address same location
-- avoid reading same address twice
if I_selS1 = R0 then
data := X"00000000";
else
data := regs(to_integer(unsigned(I_selS1)));
end if;
O_dataS1 <= data;
O_dataS2 <= data;
else
-- two different locations selected for reading
-- dual port memory should handle that just fine
if I_selS1 = R0 then
O_dataS1 <= X"00000000";
else
O_dataS1 <= regs(to_integer(unsigned(I_selS1)));
end if;
if I_selS2 = R0 then
O_dataS2 <= X"00000000";
else
O_dataS2 <= regs(to_integer(unsigned(I_selS2)));
end if;
end if;
elsif I_op = REGOP_WRITE_ALU then
-- we write data from ALU (no read involved)
if I_selD /= R0 then
regs(to_integer(unsigned(I_selD))) <= I_dataAlu;
end if;
elsif I_op = REGOP_WRITE_MEM then
-- we write data from memory (no read involved)
if I_selD /= R0 then
regs(to_integer(unsigned(I_selD))) <= I_dataMem;
end if;
end if;
end if;
end process;
end Behavioral;
This, however, does not seem to change the result regarding the pass-through logic. I still have no idea how read-during-write behavior could happen there. In the compilation log I see
READ_DURING_WRITE_MODE_MIXED_PORTS OLD_DATA Untyped 36
READ_DURING_WRITE_MODE_PORT_A NEW_DATA_NO_NBE_READ Untyped 37
READ_DURING_WRITE_MODE_PORT_B NEW_DATA_NO_NBE_READ Untyped 38
and I assume that the "NEW_DATA"-part is the reason for the pass-through. I don't know how this is determined, though.