- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
i read a lot of similar threads on this topic here but i could not find a solution to my (maybe suuper simple;) ) questions. I also would like to know if this approach is a good way to implement shared memory to pass 20 .. 30 parameters . The basic idea is to create a shared memory location for Nios to dump parameters and VHDL modules to read these parameters. So i created a dual port RAM in Qsys, connecting to the first slave only the data master from Nios and exporting the second slave. i connected a VHDL module to the second slave. The whole system looks like this: https://www.alteraforum.com/forum/attachment.php?attachmentid=8978 In Qsys the dpram was instantiated with: datawidth 32 bit, total memory size 32768 bytes, (which results in a 13 bit wide address bus) single clock operation. The Address region lasts from 0x0400_0000 to 0x0400_7FFF. I checked if i could read and write from Nios to the memory and this works fine with:
IOWR(SHARED_MEM_BASE,0,0xF);
int a = IORD(SHARED_MEM_BASE,0);
printf("%i",a); // results in 15
For the VHDL module i thought it is sufficient to connect the address where i want to read from and read the data from this address. But i struggle to map the addresses. The base_address is h"4000000" for the first memory slave but how can i now access this region through the second slave? I would need 25 bit to reach this address but Qsys only gives me 13. I have the feeling this is a really stupid question, but right now i am really stuck. i read the Avalon Specification but i could not find a solution there. Thanks to all for reading!! Cheers Tim library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity MemoryMaster is
port (
clk : in std_logic;
reset_n : in std_logic;
--av_m_chipselect : in std_logic;
av_m_write : out std_logic;
av_m_read : out std_logic;
av_m_address : out std_logic_vector(12 downto 0);
av_m_writedata : out std_logic_vector(31 downto 0);
av_m_readdata : in std_logic_vector(31 downto 0);
q : out std_logic
);
end MemoryMaster;
-- base address shared memory 0x4000 000
architecture behave of MemoryMaster is
begin
avalon_if : process (reset_n, clk)
begin
if ( reset_n = '0' ) then
av_m_address <= (others => '0');
av_m_write <= '0';
av_m_read <= '0';
av_m_writedata <= (others => '0');
elsif clk'event and clk ='1' then
--av_m_address <= (others => '0');
--av_m_address(26) <= '1';
av_m_address <=b"0_0100_0000_0000";
if (av_m_readdata = x"F") then
q <= '1';
else
q <= '0';
--if ( av_m_read_n = '0' and av_m_address = b"0" ) then
--av_m_readdata <= reg_temp;
--elsif ( av_m_write = '0' and av_m_address = b"1" ) then
end if;
end if;
end process avalon_if;
end behave;
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your MMMaster and the second port of the DPRam form a 'private' bus so the memory addresses start from 0. Your code with an address signal of width 13 is fine. One warning though: because of the way the memory blocks are designed the readdata will be delayed by (at least) one clock after applying the readaddress.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the answer!!
So the address range on my VHDL side goes from 0x0 to 0x7FFF. When i write in C to the base address of the shared memory i should be able to get this value when reading from address b"0_0000_0000_0000" in VHDL. But i do not see anything. Also the other way round, writing to memory from VHDL does not work. I apply the write byteenable and a cs signal and try to write values to different location with a 32 bit (4 byte) offset. Like addr0 := 0_0000_0000_0000 addr1 := 0_0000_0000_0100 addr2 := 0_0000_0000_1000 Since the bus is private and it is a point to point connection a CS is not necessary, but i tried just in case. Then i want to check the values in C with
for (offset = sizeof(unsigned int);( offset & (mem_size -1)) != 0 ; offset << 1 ){
printf(" %d", IORD_32DIRECT(SHARED_MEM_BASE,offset));
}
my intention was, that uploading the *sof file first, the VHDL code gets executed first and after downloading the *.elf file the C Code reads from the same addresses and returns the values. But the memory is completely empty. Am i doing it wrong with the offsets? But then is should see at least some values in memory? Or are my read and write routines wrong? I ran a memory test program on the shared memory and it passed OK so i think i do not have timing issues.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
What you describe should actually work. Without a view on the complete project it is not straightforward giving the 'good' advice.
I'm no NIOS real expert as I haven't used a NIOS II CPU yet, but have co-worked on a project where the real programmer did the NIOS code and I the rest of the HW If you have specified a data cache you have to set bit 31 of the address to bypass the cache.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- Quote Start --- Ok as an attachment i uploaded the archived project. i hope this makes it clearer. --- Quote End --- Yes, that helps ... Now there are a few issues with the Memorymaster.vhd code:
avalon_if : process(reset_n, clk)
begin
if (reset_n = '0') then
av_m_address <= (others => '0');
av_m_write <= '0';
av_m_read <= '0';
av_m_writedata <= (others => '0');
elsif clk'event and clk = '1' then
--av_m_address <= (others => '0');
--av_m_address(26) <= '1';
av_m_address <= b"0_0000_0000_0000";
if (av_m_readdata = x"F") then
q <= '1';
else
q <= '0';
--if ( av_m_read_n = '0' and av_m_address = b"0" ) then
--av_m_readdata <= reg_temp;
--elsif ( av_m_write = '0' and av_m_address = b"1" ) then
av_m_write <= '1';
av_m_chipselect <= '1';
av_m_byteenable <= "1111";
av_m_address <= b"0_0000_0000_0000";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0000_0001";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0000_0010";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0001_0000";
av_m_writedata <= X"0000000C";
av_m_address <= b"1_1111_1111_1111";
av_m_writedata <= X"0000000C";
--av_m_address <= b"0_0000_0000_0011";
--av_m_writedata <= X"0000000C";
av_m_write <= '0';
av_m_chipselect <= '0';
av_m_byteenable <= (others => '0');
end if;
end if;
end process avalon_if;
The line: if (av_m_readdata = x"F") then
will always return false as 'av_m_rreaddata' is wider than the width of x"F" From the lines: av_m_write <= '1';
av_m_chipselect <= '1';
av_m_byteenable <= "1111";
av_m_address <= b"0_0000_0000_0000";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0000_0001";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0000_0010";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0001_0000";
av_m_writedata <= X"0000000C";
av_m_address <= b"1_1111_1111_1111";
av_m_writedata <= X"0000000C";
--av_m_address <= b"0_0000_0000_0011";
--av_m_writedata <= X"0000000C";
av_m_write <= '0';
av_m_chipselect <= '0';
av_m_byteenable <= (others => '0');
only the very last assignments av_m_address <= b"1_1111_1111_1111";
av_m_writedata <= X"0000000C";
av_m_write <= '0';
av_m_chipselect <= '0';
av_m_byteenable <= (others => '0');
will be synthesized and you are inherently not writing into the DPRam, so the NIOS II will not read anything other then the data initialized at start-up which will be all zeroes after loading the sof (unless you specify a .mif file with user-data) You can see in the attached RTL-snip what the Synthesizer made out of your .vhd source.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I aligned the compare value to the databus width and now reading works perfectly !!
But i still do not understand why i cannot write. At the beginning of the process i assigned the control signals, then the address where i want to write and then the data. After all the transfers i set the control signals to zero. But is this sequential approach correct? I thought the RTL viewer shows only the last state of the signals, so the values are actually ok. When i keep the control signals all the time on high level the RTL shows the correct assignments, but the memory is still empty. https://www.alteraforum.com/forum/attachment.php?attachmentid=8987
avalon_if : process (reset_n, clk)
begin
elsif clk'event and clk ='1' then
av_m_write <= '1';
av_m_chipselect <= '1';
av_m_byteenable <= "1111";
av_m_address <= b"0_0000_0000_0000";
av_m_writedata <= X"0000000C";
end if;
end process avalon_if;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The new code shown will write the value 0x0000000C to address 0 continuously.
In the code:else
q <= '0';
--if ( av_m_read_n = '0' and av_m_address = b"0" ) then
--av_m_readdata <= reg_temp;
--elsif ( av_m_write = '0' and av_m_address = b"1" ) then
av_m_write <= '1';
av_m_chipselect <= '1';
av_m_byteenable <= "1111";
av_m_address <= b"0_0000_0000_0000";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0000_0001";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0000_0010";
av_m_writedata <= X"0000000C";
av_m_address <= b"0_0000_0001_0000";
av_m_writedata <= X"0000000C";
av_m_address <= b"1_1111_1111_1111";
av_m_writedata <= X"0000000C";
--av_m_address <= b"0_0000_0000_0011";
--av_m_writedata <= X"0000000C";
av_m_write <= '0';
av_m_chipselect <= '0';
av_m_byteenable <= (others => '0');
end if;
the synthesiser will only honour the 'last' assignment for each signal, thus of al the address assignments in the above code : av_m_address <= b"0_0000_0000_0000";
av_m_address <= b"0_0000_0000_0001";
av_m_address <= b"0_0000_0000_0010";
av_m_address <= b"0_0000_0001_0000";
av_m_address <= b"1_1111_1111_1111";
only the very last will be synthesised as it 'wins'. If you want to do multiple writes you have to do a write per clock cycle and you have to understand sequential coding: I suggest you read pages 91-121 of V. Pedroni's book "Circuit Design with VHDL".
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
in the book there is a pretty nice example of how to access a RAM in VHDL. I see now that my code has some flaws.
And i think now i have a good idea of how to implement multiple r/w operations. But i have one question for the nios side. When i constantly write to the base address i should be able the see this value when i scan the addresses. But it is empty. I implemented an init function to write direct once, but still no results. Is a sole IORD(baseaddr,0x0) the right way to do so?- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I would connect the memory to the Nios as 'tightly coupled data memory' and then access it directly using normal C pointers (read up on the 'volatile' keyword).
For effficency define a single C struct for the shared memory area and use a global register variable pointing to the base for all accesses. If you try hard enough you can use signaltap to monitor the nios's accesses to the tightly coupled memory.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I first tried to deactivate the cache then after changed the memory to tightly coupled memory but it still did not work. What i did not do from the beginning was setting the clken bit to one. And that finally solved my problem now i can write to RAM through VHDL. I neglected it because i read that this signal cannot be used in dual port mode with a single clock. I assume this assumtion is wrong but i did not find an explainationo for this type in the avalon spec or in the on-chip memory implementation manual. Could you explain it? Thank you two, helped me a loot :)
Cheers Tim- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Should be documented somewhere.
From memory: In dual-clock mode there is a clken for each clock/port In single-clock mode there is an 'address hold' signal for each port. These have much the same function - although they are inverted. The nios uses these (on TCM) to hold the read data when there is a pipeline stall (since the address is already updated to that for the next instruction). At a guess there is also a global clken that would affect both ports - so you wouldn't normall want to use it. David- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
hi all, i have problem with dual port ram to, that is when i comple dual port ram theey siad that i havr to use share variable for this, but as i know share variable just used for simulation, if i want to use for synthesizion and download this dual port ram to the board, how to do it ???
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
i do not really understand your question. can you be a bit more precise?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
library ieee;
use ieee.std_logic_1164.all;
entity true_dpram_sclk is
port
(
data_a : in std_logic_vector(7 downto 0);
data_b : in std_logic_vector(7 downto 0);
addr_a : in natural range 0 to 63;
addr_b : in natural range 0 to 63;
we_a : in std_logic := '1';
we_b : in std_logic := '1';
clk : in std_logic;
q_a : out std_logic_vector(7 downto 0);
q_b : out std_logic_vector(7 downto 0)
);
end true_dpram_sclk;
architecture rtl of true_dpram_sclk is
-- Build a 2-D array type for the RAM
subtype word_t is std_logic_vector(7 downto 0);
type memory_t is array(63 downto 0) of word_t;
-- Declare the RAM
shared variable ram : memory_t;
begin
-- Port A
process(clk)
begin
if(rising_edge(clk)) then
if(we_a = '1') then
ram(addr_a) := data_a;
end if;
q_a <= ram(addr_a);
end if;
end process;
-- Port B
process(clk)
begin
if(rising_edge(clk)) then
if(we_b = '1') then
ram(addr_b) := data_b;
end if;
q_b <= ram(addr_b);
end if;
end process;
end rtl;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
There is nothing wrong with using a shared variable here. It is often used to get write-before-read behaviour from ram. If you use a signal, you get read before write.
Quartus will synthesise '93 shared variables just fine.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Shared variables are usually not recommended for synthesis, as it is considered bad design practise, but it can be synthesized. If you need a dual clock RAM with write access from both clock domains, a shared variable is actually the only way to do it in VHDL. Sometimes we have to make exceptions on bad design practises ;)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
ok me again. Now i have a question about writing and reading from VHDL to memory. I designed a state machine to handel multiple rd/wr to different address locations.
The idea is to write the counter to the first 10 locations. After that it should switch into the read state and stay there forever. There outputs depending to the value i write from C should be toggled. When i start a memory scan i C i see something in memory but not the desired counter values. In the read state the ports q1 and q2 work correct only when i write in C to the base_address. But it should be working by writing to the first register so base_address +0x4. I think i do not really apply the next address to the memory module. Do you have an idea? Cheers, Tim
type state_type is (addr_inc,wr,idle,rd);
signal pr_state, nx_state : state_type;
signal av_m_address_i : std_logic_vector(12 downto 0);
signal k_p : std_logic_vector(31 downto 0);
signal counter : integer := 0;
begin
process(clk, reset)
BEGIN
if reset = '1' then
pr_state <= idle;
elsif clk'event and clk ='1' then
pr_state <= nx_state;
end if;
END PROCESS;
process (pr_state)
BEGIN
case pr_state is
when idle =>
nx_state <= wr;
av_m_address_i <= b"0_0000_0000_0000";
counter <= 0;
when wr => -- increment the counter value and write it to the address
counter <= counter + 1;
av_m_write <= '1';
av_m_clken <= '1';
av_m_chipselect <= '1';
av_m_byteenable <= X"1";
av_m_address <= av_m_address_i;
av_m_writedata <= std_logic_vector(to_signed(counter,32));
counter <= counter + 1;
if counter = 10 then
nx_state <= rd;
else
nx_state <= addr_inc;
end if;
when addr_inc =>
av_m_address_i <= std_logic_vector(unsigned(av_m_address_i) + b"0_0000_0000_0100");
nx_state <= wr;
when rd =>
av_m_address <= b"0_0000_0000_0100";
if av_m_readdata = X"00000000" then
q1 <= '1';
q2 <= '0';
elsif av_m_readdata = X"00000001" then
q1 <= '0';
q2 <= '1';
else
q1 <= '1';
q2 <= '1';
end if;
nx_state <= rd;
when others =>
nx_state <= wr;
end case;
END PROCESS;
end behave;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
For better locating the error i changed the design to two states, read,idle.
So by resetting the design it goes into idle and from there directly to read. The netlist viewer shows that the correct address is assigned to the output pin which is then connected to the dual port memory module of nios. Then i would expect the readdata value comes from this address. But it it still just responding when writing to the base address. I think i miss some super obvious ?!? (: https://www.alteraforum.com/forum/attachment.php?attachmentid=9011- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You never disable the write signal in your process. This means that you will continue to write values even in the read state. This code should generate a lot of latches warnings, as you aren't assigning values to every signal in every state of your state machine. I suggest that you add some default values before the case statement for every output signal used in the process to avoid this problem. You can use '0' as default value for the control signals (read, write, chip select, byte enable) and just put them to '1' when needed.
Also remember that there is at least one latency cycle when reading from the memory, so the read state should actually be two states: one to set the address, and one to decode the read back value.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Daixiwen,
yes you were right. I had a lot of warnings for latches. I redesigned the state machine to one porcess holding the complete structure.I also added default values before the case statement. Now i do not get the warnings anymore. However what i am still struggling with is multiple reading from different address. When the control signal master_setup is active, the value in the address 0x10 defines with components get initialized. (state setup_eval_var). From there multiple variables are read from fixed memory locations and assigned to output ports. ( halfbridge_freq_scale halfbridge_duty) But here wrong values are read and i do not know why. When i downscale the system to only one variable everything works fine. So for example reading only halfbridge_freq_scale the system works fine. The component them self work fine. Simulation with Modelsim or running with constants shows perfect behavior. Am i doing something wrong with the multiple address incrementation?
case state is
when idle =>
if master_setup ='1' then
state <= setup_eval_var_addr;
elsif master_run ='1' then
state <= setup_run_addr;
elsif master_wr ='1' then
state <= setup_write_addr;
else
state <= idle;
end if;
when setup_eval_var_addr =>
av_m_clken <= '1';
av_m_cs <= '1';
av_m_byteenable <= X"F";
av_m_address <= b"0_0000_0001_0000";
state <= setup_eval_var;
when setup_eval_var =>
av_m_clken <= '1';
av_m_cs <= '1';
av_m_byteenable <= X"F";
if av_m_rddata = X"00000001" then --read halfbridge vars
state <= setup_hb_freq_addr;
elsif av_m_rddata = X"00000002" then
state <= setup_dp_t1_addr;
elsif av_m_rddata = X"00000003" then
state <= setup_npc_freq_addr; --tbd
else
state <= idle;
end if;
-- HALFBRIDGE states for reading the desired signal frequency
when setup_hb_freq_addr =>
av_m_clken <= '1';
av_m_cs <= '1';
av_m_byteenable <= X"F";
av_m_address <= b"0_0000_0100_0000"; --64
state <= rd_hb_freq;
when rd_hb_freq =>
av_m_clken <= '1';
av_m_cs <= '1';
av_m_byteenable <= X"F";
halfbridge_freq_scale <= av_m_rddata; --output port to halfbridge component
state <= setup_hb_duty_addr;
-- HALFBRIDGE states for reading the signal duty cycle value
when setup_hb_duty_addr =>
av_m_clken <= '1';
av_m_cs <= '1';
av_m_byteenable <= X"F";
av_m_address <= b"0_0000_0001_1000"; --24
state <= rd_hb_duty;
when rd_hb_duty =>
av_m_clken <= '1';
av_m_cs <= '1';
av_m_byteenable <= X"F";
halfbridge_duty <= av_m_rddata;
if master_rd ='1' then
state <= setup_hb_freq_addr;
else
state <= idle;
end if;
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page